package br.edu.ufcg.splab.simulator.jgrapht;

import java.io.File;
import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.jgrapht.UndirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleGraph;

import br.edu.ufcg.splab.core.InterfaceGenerate;
import br.edu.ufcg.splab.core.InterfaceGraph;
import br.edu.ufcg.splab.core.graph.Tree;
import br.edu.ufcg.splab.exceptions.LTSBTException;
import br.edu.ufcg.splab.generator.ConvertGraphToTree;
import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestCaseGenerator;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.io.TGFWriter;
import br.edu.ufcg.splab.parser.GraphToAnnotatedGraphParser;
import br.edu.ufcg.splab.techniques.reduction.functions.similarity.DistanceFunction;
import br.edu.ufcg.splab.techniques.reduction.functions.similarity.LevenshteinFunction;
import br.edu.ufcg.splab.techniques.reduction.strategies.SimilarityMatrix;
import br.edu.ufcg.splab.util.Controller;
import br.edu.ufcg.splab.util.MapFaultAndTestSuite;
import br.edu.ufcg.splab.util.ReadTGF;

public class UndirectedGraphGenerator {

	public UndirectedGraph<String, DefaultEdge> createUndirectedGraph(
			Map<Integer, List<Integer>> map) {
		SimpleGraph<String, DefaultEdge> graph = new SimpleGraph<String, DefaultEdge>(
				DefaultEdge.class);
		Set<Entry<Integer, List<Integer>>> set = map.entrySet();
		Iterator<Entry<Integer, List<Integer>>> it = set.iterator();
		while (it.hasNext()) {
			Entry<Integer, List<Integer>> entry2 = it.next();
			List<Integer> list = entry2.getValue();
			for (int i = 0; i < list.size(); i++) {
				if (!graph.containsVertex(Integer.toString(list.get(i)))) {
					graph.addVertex(Integer.toString(list.get(i)));
				}
			}
			for (int i = 0; i < list.size() - 1; i++) {
				for (int j = i + 1; j < list.size(); j++) {
					graph.addEdge(Integer.toString(list.get(i)),
							Integer.toString(list.get(j)));
				}
			}
		}

		return graph;
	}

	/*
	 * Creates an undirected graph from the test cases that fail and the
	 * relationship among this test cases.
	 */
	public UndirectedGraph<String, DefaultEdge> createUndirectedGraph(
			TestSuite generatedTestSuite,
			TestSuite testCases, DistanceFunction function, double percentage) {
		
		Map<Integer, TestCase> mapTestSuite = createMapTestSuite(generatedTestSuite);
		
		
		
		SimilarityMatrix matrix = new SimilarityMatrix();
		double[][] m = matrix.createMatrix(testCases, function);

		SimpleGraph<String, DefaultEdge> graph = new SimpleGraph<String, DefaultEdge>(
				DefaultEdge.class);

		List<Integer> vertices = new ArrayList<Integer>();
		for (TestCase tc : testCases.getTestSuite()) {
			vertices.add(this.getValueTestCase(tc, mapTestSuite));
			graph.addVertex(Integer.toString(this.getValueTestCase(tc,
					mapTestSuite)));

		}

		double minimumValue = getMinimumValue(generatedTestSuite, function,	percentage);
		BigDecimal mv = new BigDecimal(minimumValue).setScale(3, RoundingMode.HALF_EVEN);
		for (int line = 0; line < m.length; line++) {
			for (int column = line + 1; column < m.length; column++) {
				BigDecimal mlc = new BigDecimal(m[line][column]).setScale(3, RoundingMode.HALF_EVEN);
				if (mlc.doubleValue() >= mv.doubleValue()) {
					Integer v1 = vertices.get(line);
					Integer v2 = vertices.get(column);
					graph.addEdge(v1.toString(), v2.toString());
				}
			}
		}

		return graph;
	}
	
	/*
	 * Creates the map to which the test cases are mapped.
	 */
	private Map<Integer, TestCase> createMapTestSuite(TestSuite testSuite) {
		Map<Integer, TestCase> mapTestSuite = new HashMap<Integer, TestCase>();
		int i = 1;
		for (TestCase testCase : testSuite.getTestSuite()) {
			mapTestSuite.put(i++, testCase);
		}
		return mapTestSuite;
	}

	/*
	 * Returns the key to which the specified test case is mapped, or -1 if this
	 * map contains no mapping for the test case.
	 * 
	 * @param testCase The test case whose associated key is to be returned.
	 * 
	 * @return The key to which the specified test case is mapped or -1 if this
	 * map contains no mapping for the test case.
	 */
	private int getValueTestCase(TestCase testCase,
			Map<Integer, TestCase> mapTestSuite) {
		if (mapTestSuite.containsValue(testCase)) {
			Set<Entry<Integer, TestCase>> set = mapTestSuite.entrySet();
			for (Entry<Integer, TestCase> entry : set) {
				if (entry.getValue().equals(testCase)) {
					return entry.getKey();
				}
			}
		}
		;
		return -1;
	}

	/*
	 * Sets the minimum value for a strong relation between two test cases on
	 * matrix from of a distance function.
	 * 
	 * @return The minimum value to define a strong relationship between two
	 * test cases.
	 */
	private double getMinimumValue(TestSuite testSuite, DistanceFunction function,
			double percentageMinimum) {
		SimilarityMatrix matrix = new SimilarityMatrix();
		double maximumValue = matrix.getMaximumValue(testSuite, function);
		return maximumValue * percentageMinimum;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		try {
			String path = "./test-data/failures_identifier/defect_models_all/";
			String pathOutput1 = "./test-data/failures_identifier/cliques_new/";
			
			File filePath = new File(path);
			List<File> fileList = new ArrayList<File>();
			File[] files = filePath.listFiles();
			List<File> fileandFolderList = Arrays.asList(files);
			for (File file : fileandFolderList) {
				if (file.getName()
						.substring(file.getName().length() - 3,
								file.getName().length()).equals("tgf")) {
					fileList.add(file);
				}
			}

			LevenshteinFunction l = new LevenshteinFunction();

			for (File file : fileList) {
				System.out.println(file.getName());
				
				TGFWriter w = new TGFWriter();

				ReadTGF read = new ReadTGF();
				InterfaceGraph graph = read.getGraph(file.getPath());
				GraphToAnnotatedGraphParser convertsAnnotatedGraphToTree = new GraphToAnnotatedGraphParser();
				InterfaceGraph newGraph = null;

				newGraph = convertsAnnotatedGraphToTree.graphContraction(graph);
				ConvertGraphToTree convertsGraphToTree = new ConvertGraphToTree();
				Tree tree = convertsGraphToTree.converterGraph(newGraph);
				
				tree.expand(1);

				InterfaceGenerate generator = new TestCaseGenerator();
				TestSuite generatedSuite = generator.generate(tree);
				Controller controller = new Controller();
				TestSuite validTestSuite = controller
						.getValidTS(generatedSuite);
				
				MapFaultAndTestSuite mTS = new MapFaultAndTestSuite(
						generatedSuite);


				CliqueFinder clique = new CliqueFinder();
				SimilarityMatrix matrix = new SimilarityMatrix();
				System.out.println(matrix.printMatrix(matrix.createMatrix(validTestSuite, l), validTestSuite.getTestSuite(), mTS));
		
				double minimumValue = matrix.getMaximumValue(validTestSuite, l) * 0.75;
				System.out.println("MaximumValue: " + matrix.getMaximumValue(validTestSuite, l));
				System.out.println("minimumValue: " + minimumValue);
		
				Collection<Set<TestCase>> cliques = clique.getAllMaximalCliques(validTestSuite, l, minimumValue);		
				
				Collection<Set<TestCase>> allCliques = clique.getAllCliques(validTestSuite, l, minimumValue);		
				
				System.out.println("maximal cliques: " + cliques.size());
				System.out.println("All cliques: " + allCliques.size());
				
				
				w.writeAllCliques(cliques, mTS, pathOutput1
								+ file.getName().substring(0,
										file.getName().length() - 4)
								+ "_complete_generated.tgf");
				w.writeAllCliques(allCliques, mTS, pathOutput1
								+ file.getName().substring(0,
										file.getName().length() - 4)
								+ "_complete_generatedAllCliques.tgf");
			}
		} catch (LTSBTException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
	}

}
